﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

namespace GamePlusClassLibrary
{
    public class DataAccessLayer
    {
        //public static string CONN_STRING = ConfigurationManager.ConnectionStrings["CONN_STRING"].ToString();

        private static string _conn_String = string.Empty;

        public static string CONN_STRING
        {
            get
            {
                if (!string.IsNullOrEmpty(_conn_String))
                    return _conn_String;
                else
                {
                    return ConfigurationManager.ConnectionStrings["CONN_STRING"].ToString();
                }
            }
            set
            {
                if (!string.IsNullOrEmpty(value))
                    _conn_String = ConfigurationManager.ConnectionStrings[value].ToString();
                else
                    _conn_String = ConfigurationManager.ConnectionStrings["CONN_STRING"].ToString();
            }
        }


        #region ExecuteNonQuery
        /// <summary>
        /// Método utilizado para executar um comando de escrita no banco de dados.
        /// </summary>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>Inteiro com o resultado do método original.</returns>
        public static int ExecuteNonQuery(CommandType cmdType, string cmdText, List<SqlParameter> cmdParms)
        {
            SqlCommand cmd = new SqlCommand();
            //SqlCommand cmd_log;
            //SqlCommand cmd_dellog;
            using (SqlConnection conn = new SqlConnection(CONN_STRING))
            {
                try
                {
                    //CriaLogCmd(conn, out cmd_log);
                    //DeletarLogCmd(conn, out cmd_dellog);
                    //cmd_log.ExecuteNonQuery();
                    PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                    int val = cmd.ExecuteNonQuery();
                    //cmd_dellog.ExecuteNonQuery();
                    //int val = ExecuteNonQueryReTry(cmd);
                    cmd.Parameters.Clear();
                    return val;
                }
                catch (SqlException ex)
                {
                    if (ex.Message.Contains("TCP Provider, error: 0") || ex.Message.Contains("Provedor TCP, error: 0"))
                    {
                        try
                        {
                            conn.Close();
                            SqlConnection.ClearAllPools();
                            //cmd = new SqlCommand();
                            //PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                            //CriaLogCmd(conn, out cmd_log);
                            //DeletarLogCmd(conn, out cmd_dellog);
                            //cmd_log.ExecuteNonQuery();
                            int val = cmd.ExecuteNonQuery();
                            //cmd_dellog.ExecuteNonQuery();
                            cmd.Parameters.Clear();
                            return val;
                        }
                        catch
                        {
                            conn.Close();
                            conn.Dispose();
                            throw;
                        }
                    }
                    else
                    {
                        throw;
                    }
                }
                catch
                {
                    conn.Close();
                    conn.Dispose();
                    throw;
                }
            }
        }
        /// <summary>
        /// Método utilizado para executar um comando de escrita no banco de dados, dentro de uma transação.
        /// </summary>
        /// <param name="trans">Transação corrente.</param>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>Inteiro com o resultado do método original.</returns>
        public static int ExecuteNonQuery(SqlTransaction trans, CommandType cmdType, string cmdText, List<SqlParameter> cmdParms)
        {
            SqlCommand cmd = new SqlCommand();
            PrepareCommand(cmd, trans.Connection, trans, cmdType, cmdText, cmdParms);
            int val = cmd.ExecuteNonQuery();
            cmd.Parameters.Clear();
            return val;
        }
        #endregion

        #region ExecuteNonQueryCmd
        /// <summary>
        /// Método utilizado para executar um comando de escrita no banco de dados.
        /// </summary>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>Comando SQL executado.</returns>
        public static SqlCommand ExecuteNonQueryCmd(CommandType cmdType, string cmdText, List<SqlParameter> cmdParms)
        {
            SqlCommand cmd = new SqlCommand();
            using (SqlConnection conn = new SqlConnection(CONN_STRING))
            {
                try
                {
                    PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                    cmd.ExecuteNonQuery();
                }
                catch (SqlException ex)
                {
                    if (ex.Message.Contains("TCP Provider, error: 0") || ex.Message.Contains("Provedor TCP, error: 0"))
                    {
                        try
                        {
                            conn.Close();
                            SqlConnection.ClearAllPools();
                            //PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                            cmd.ExecuteNonQuery();
                        }
                        catch
                        {
                            conn.Close();
                            conn.Dispose();
                            throw;
                        }
                    }
                    else
                    {
                        throw;
                    }
                }
                catch
                {
                    conn.Close();
                    conn.Dispose();
                    throw;
                }
            }
            return cmd;
        }
        /// <summary>
        /// Método utilizado para executar um comando de escrita no banco de dados, dentro de uma transação.
        /// </summary>
        /// <param name="trans">Transação corrente.</param>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>Comando SQL executado.</returns>
        public static SqlCommand ExecuteNonQueryCmd(SqlTransaction trans, CommandType cmdType, string cmdText, List<SqlParameter> cmdParms)
        {
            SqlCommand cmd = new SqlCommand();
            PrepareCommand(cmd, trans.Connection, trans, cmdType, cmdText, cmdParms);
            cmd.ExecuteNonQuery();
            return cmd;
        }
        #endregion

        #region ExecuteReader
        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados.
        /// </summary>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <param name="connStringKey">Connection string que deve ser usada.</param>
        /// <returns>SqlDataReader de retorno da consulta.</returns>
        public static SqlDataReader ExecuteReader(CommandType cmdType, string cmdText, List<SqlParameter> cmdParms, string connStringKey)
        {
            string connString = connStringKey;
            //string connString = ConfigurationManager.ConnectionStrings[connStringKey].ConnectionString;
            SqlDataReader dr = null;
            SqlConnection conn = new SqlConnection(connString);
            SqlCommand cmd = new SqlCommand();
            try
            {
                PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
            }
            catch (SqlException ex)
            {
                if (ex.Message.Contains("TCP Provider, error: 0") || ex.Message.Contains("Provedor TCP, error: 0"))
                {
                    try
                    {
                        conn.Close();
                        SqlConnection.ClearAllPools();
                        //SqlCommand cmd = new SqlCommand();
                        //PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                        dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                    }
                    catch
                    {
                        conn.Close();
                        conn.Dispose();
                    }
                }
                else
                {
                    throw;
                }
            }
            catch
            {
                conn.Close();
                conn.Dispose();
                throw;
            }
            return dr;
        }
        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados.
        /// </summary>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>SqlDataReader de retorno da consulta.</returns>
        public static SqlDataReader ExecuteReader(CommandType cmdType, string cmdText, List<SqlParameter> cmdParms)
        {
            //SqlDataReader dr = null;
            SqlConnection conn = new SqlConnection(CONN_STRING);
            SqlCommand cmd = new SqlCommand();
            try
            {
                PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                return cmd.ExecuteReader(CommandBehavior.CloseConnection);

            }
            catch (SqlException ex)
            {
                if (ex.Message.Contains("TCP Provider, error: 0") || ex.Message.Contains("Provedor TCP, error: 0"))
                {
                    try
                    {
                        conn.Close();
                        SqlConnection.ClearAllPools();
                        conn.Open();
                        //SqlCommand cmd = new SqlCommand();
                        //PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                        return cmd.ExecuteReader(CommandBehavior.CloseConnection);
                    }
                    catch
                    {
                        conn.Close();
                        conn.Dispose();
                        throw;
                    }
                }
                else
                {
                    throw;
                }
            }
            catch
            {
                conn.Close();
                conn.Dispose();
                throw;
            }
        }
        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados, dentro de uma transação.
        /// </summary>
        /// <param name="trans">Transação corrente.</param>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>SqlDataReader de retorno da consulta.</returns>
        public static SqlDataReader ExecuteReader(SqlTransaction trans, CommandType cmdType, string cmdText, List<SqlParameter> cmdParms)
        {
            SqlDataReader dr = null;
            SqlCommand cmd = new SqlCommand();
            PrepareCommand(cmd, trans.Connection, trans, cmdType, cmdText, cmdParms);
            dr = cmd.ExecuteReader();
            return dr;
        }
        #endregion

        #region ExecuteReaderDs
        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados.
        /// </summary>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <param name="connStringKey">Connection string que deve ser utilizada.</param>
        /// <returns>DataSet de retorno da consulta.</returns>
        public static DataSet ExecuteReaderDs(CommandType cmdType, string cmdText, List<SqlParameter> cmdParms, string connStringKey)
        {
            string connString = ConfigurationManager.ConnectionStrings[connStringKey].ConnectionString;
            DataSet ds = new DataSet();
            SqlCommand cmd = new SqlCommand();
            using (SqlConnection conn = new SqlConnection(connString))
            {
                try
                {
                    PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                    SqlDataAdapter da = new SqlDataAdapter(cmd);
                    da.Fill(ds);
                    cmd.Parameters.Clear();
                }
                catch (SqlException ex)
                {
                    if (ex.Message.Contains("TCP Provider, error: 0") || ex.Message.Contains("Provedor TCP, error: 0"))
                    {
                        try
                        {
                            conn.Close();
                            SqlConnection.ClearAllPools();
                            //SqlCommand cmd = new SqlCommand();
                            //PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                            SqlDataAdapter da = new SqlDataAdapter(cmd);
                            da.Fill(ds);
                            cmd.Parameters.Clear();
                        }
                        catch
                        {
                            conn.Close();
                            conn.Dispose();
                            throw;
                        }
                    }
                    else
                    {
                        throw;
                    }
                }
                catch
                {
                    conn.Close();
                    conn.Dispose();
                    throw;
                }
            }
            return ds;
        }
        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados.
        /// </summary>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>DataSet de retorno da consulta.</returns>
        public static DataSet ExecuteReaderDs(CommandType cmdType, string cmdText, List<SqlParameter> cmdParms)
        {
            DataSet ds = new DataSet();
            SqlCommand cmd = new SqlCommand();
            using (SqlConnection conn = new SqlConnection(CONN_STRING))
            {
                try
                {
                    PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                    SqlDataAdapter da = new SqlDataAdapter(cmd);
                    da.Fill(ds);
                    cmd.Parameters.Clear();
                }
                catch (SqlException ex)
                {
                    if (ex.Message.Contains("TCP Provider, error: 0") || ex.Message.Contains("Provedor TCP, error: 0"))
                    {
                        try
                        {
                            conn.Close();
                            SqlConnection.ClearAllPools();
                            //SqlCommand cmd = new SqlCommand();
                            //PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                            SqlDataAdapter da = new SqlDataAdapter(cmd);
                            da.Fill(ds);
                            cmd.Parameters.Clear();
                        }
                        catch
                        {
                            conn.Close();
                            conn.Dispose();
                            throw;
                        }
                    }
                    else
                    {
                        throw;
                    }
                }
                catch
                {
                    conn.Close();
                    conn.Dispose();
                    throw;
                }
            }
            return ds;
        }
        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados, dentro de uma transação.
        /// </summary>
        /// <param name="trans">Transação corrente.</param>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>DataSet de retorno da consulta.</returns>
        public static DataSet ExecuteReaderDs(SqlTransaction trans, CommandType cmdType, string cmdText, List<SqlParameter> cmdParms)
        {
            DataSet ds = new DataSet();
            SqlCommand cmd = new SqlCommand();
            PrepareCommand(cmd, trans.Connection, trans, cmdType, cmdText, cmdParms);
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            da.Fill(ds);
            cmd.Parameters.Clear();
            return ds;
        }
        #endregion

        #region ExecuteScalar
        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados.
        /// </summary>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>Objeto de retorno da consulta.</returns>
        public static object ExecuteScalar(CommandType cmdType, string cmdText, List<SqlParameter> cmdParms)
        {
            object obj = new object();
            SqlCommand cmd = new SqlCommand();
            using (SqlConnection conn = new SqlConnection(CONN_STRING))
            {
                try
                {
                    PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                    obj = cmd.ExecuteScalar();
                    cmd.Parameters.Clear();
                }
                catch (SqlException ex)
                {
                    if (ex.Message.Contains("TCP Provider, error: 0") || ex.Message.Contains("Provedor TCP, error: 0"))
                    {
                        try
                        {
                            conn.Close();
                            SqlConnection.ClearAllPools();
                            //SqlCommand cmd = new SqlCommand();
                            //PrepareCommand(cmd, conn, null, cmdType, cmdText, cmdParms);
                            obj = cmd.ExecuteScalar();
                            cmd.Parameters.Clear();
                        }
                        catch
                        {
                            conn.Close();
                            conn.Dispose();
                            throw;
                        }
                    }
                    else
                    {
                        throw;
                    }
                }
                catch
                {
                    conn.Close();
                    conn.Dispose();
                    throw;
                }
            }
            return obj;
        }
        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados, dentro de uma transação.
        /// </summary>
        /// <param name="trans">Transação corrente.</param>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>Objeto de retorno da consulta.</returns>
        public static object ExecuteScalar(SqlTransaction trans, CommandType cmdType, string cmdText, List<SqlParameter> cmdParms)
        {
            SqlCommand cmd = new SqlCommand();
            PrepareCommand(cmd, trans.Connection, trans, cmdType, cmdText, cmdParms);
            object obj = cmd.ExecuteScalar();
            cmd.Parameters.Clear();
            return obj;
        }
        #endregion

        #region PrepareCommand
        /// <summary>
        /// Método utilizado para preparar os comandos SQL que serão executados no banco.
        /// </summary>
        /// <param name="cmd">Comando SQL.</param>
        /// <param name="conn">Conexão que será utilizada.</param>
        /// <param name="trans">Transação corrente.</param>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da stored procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        private static void PrepareCommand(SqlCommand cmd, SqlConnection conn, SqlTransaction trans, CommandType cmdType, string cmdText, List<SqlParameter> cmdParms)
        {
            if (conn.State != ConnectionState.Open)
                conn.Open();

            cmd.Connection = conn;
            cmd.CommandText = cmdText;

            if (trans != null)
                cmd.Transaction = trans;

            cmd.CommandType = cmdType;

            if (cmdParms != null)
            {
                foreach (SqlParameter parm in cmdParms)
                {
                    if (cmd.Parameters.Contains(parm))
                        cmd.Parameters[parm.ParameterName] = parm;
                    else
                        cmd.Parameters.Add(parm);
                }
            }
        }
        #endregion

    }
}
